;;; Extensions to the native objects defined in LUI

(in-package :lui)

(defclass native-animated-window (native-window)
  ((thread-run-loop :foreign-type :id
		    :accessor window-thread-run-loop)
   (animation-timer :foreign-type :id
		    :accessor window-animation-timer)
   (close-ack :initform nil :accessor window-close-ack :initarg :close-ack))
  (:metaclass ns:+ns-object))

;;; This does the animation, by adding the animation and close timers
;;; to the current thread's runloop and then running that runloop.
;;; The only input sources should be those timers; when the window
;;; closes, they'll be removed and the runloop will exit.

(defparameter *animation-timer-delay* 0.05d0)

(defmethod animate-window ((self native-animated-window) view done)
  (let* ((runloop (#/currentRunLoop ns:ns-run-loop)))
    (setf (window-thread-run-loop self) runloop)
    (setf (window-animation-timer self)
          (make-instance 'ns:ns-timer
			 :with-fire-date (#/distantPast ns:ns-date)
			 :interval *animation-timer-delay*
			 :target view
			 :selector (ccl::@selector #/doAnimation:)
			 :user-info (%null-ptr)
			 :repeats t))
    (ccl::with-autorelease-pool
      (#/addTimer:forMode: runloop (window-animation-timer self) #$NSDefaultRunLoopMode)
      (signal-semaphore done)
      (#/run (the ns:ns-run-loop runloop)))))


(objc:defmethod (#/stopAnimation: :void)
    ((self native-animated-window) (timer :id))
  (declare (ignore timer))
  (when (not (ccl:%null-ptr-p (window-animation-timer self))) 
    (log5:log-for (trace) "Stopping animation timer..")
    (#/invalidate (window-animation-timer self)))
  (log5:log-for (trace) "Stopping animation..")
  (let* ((cfrunloop (#/getCFRunLoop (#/currentRunLoop ns:ns-run-loop))))
    (#_CFRunLoopStop cfrunloop))
  (signal-semaphore (window-close-ack self)))

(objc:defmethod (#/close :void)
    ((self native-animated-window))
  (let* ((timer (make-instance 'ns:ns-timer
			       :with-fire-date (#/distantPast ns:ns-date)
			       :interval 0.0d0
			       :target self
			       :selector (ccl::@selector #/stopAnimation:)
			       :user-info (%null-ptr)
			       :repeats nil)))
    ;; Add the timer to the animation thread's run loop
    (log5:log-for (trace) "Closing animated window!")
    (#/addTimer:forMode: (window-thread-run-loop self) timer #$NSDefaultRunLoopMode)
    ;; Wait for it to acknowledge the close request
    (wait-on-semaphore (window-close-ack self))
    ;; Actually close the window ...
    (call-next-method)))

;;;

(objc:defmethod (#/doAnimation: :void)
    ((self native-opengl-view) (sender :id))
  (declare (ignore sender))
;  (log5:log-for (trace) "doAnimation.")
  (let ((view (lui-view self)))
    (animate-opengl-view-once view)))
					;(with-glcontext view
					;     (clear-background view)
					;    (draw view))))


;;;

(defmethod make-native-object ((self xlui::animated-application-window))
  (ccl::with-autorelease-pool
      (let ((Window (make-instance 'native-animated-window
                      :lui-window Self
                      :with-content-rect (ns:make-ns-rect 0 0 (width Self) (height Self))
                      :style-mask (logior (if (title Self) #$NSTitledWindowMask 0)
                                          (if (closeable Self) #$NSClosableWindowMask 0)
                                          (if (resizable Self) #$NSResizableWindowMask 0)
                                          (if (minimizable Self) #$NSMiniaturizableWindowMask 0))
                      :backing #$NSBackingStoreBuffered
		      :close-ack (make-semaphore)
                      :defer t)))
	(setf (window-thread-run-loop window) (#/currentRunLoop ns:ns-run-loop))
        (setf (native-window Self) Window)  ;; need to have this reference for the delegate to be in place
        (setf (native-view Self) (make-instance 'native-window-view :lui-window Self))
        ;; setup delegate
        (setf (delegate Window) (make-instance 'window-delegate :lui-window Self))
        (#/setDelegate: Window (delegate Window))
        ;; content view
        (#/setContentView: Window (#/autorelease (native-view Self)))
        (#/setTitle: Window (native-string (title Self)))
        (ns:with-ns-size (Position (x Self) (- (screen-height Self)  (y Self)))
          (#/setFrameTopLeftPoint: (native-window Self) Position))
        (when (track-mouse Self) (#/setAcceptsMouseMovedEvents: (native-window Self) #$YES))
        Window)))